\section{Elméleti háttér}

\subsection{Képek ábrázolása}
A képeket egy kétdimenziós mátrix segítségével ábrázolhatjuk, melynek elemei egész számok.
Egy pixel tárolására 24 bit (3 bájt) áll rendelkezésre, egy-egy bájt a piros, zöld, és kék színeknek.

8 bit segítségével $2^8$, azaz 256 féle árnyalatot tudunk ábrázolni egy színen belül.
A három szín értékeit egy egész számmá alakítjuk, így alakul ki egy pixel, a pixelekből pedig a kép.

\subsubsection{RGB színábrázolás}
Az RGB \cite{rgb_wiki} (Red Green Blue) színábrázolás egy additív színábrázolás, ahol a piros, zöld, kék színek "összeadásával" (addíció) tudjuk a színek széles skáláját előállítani.

\begin{figure}[H]
	\includegraphics{pics/rgb.jpg}
	\caption{Additív színábrázolás, színek keverése}
\end{figure}

\subsubsection{Példa}
Állítsuk elő a fehér színt!

A fehér színt a piros, zöld, kék színek legerősebb árnyalataiból kapjuk.

\begin{description}
	\item A piros szín
	\begin{itemize}
		\item hexadecimális alakja: \verb|ff0000|
		\item bináris alakja: $\begin{array}{c|c|c} 11111111 & 00000000 & 00000000 \end{array}$
	\end{itemize}
	\item A zöld szín
	\begin{itemize}
		\item hexadecimális alakja: \verb|00ff00|
		\item bináris alakja: $\begin{array}{c|c|c} 00000000 & 11111111 & 00000000 \end{array}$
	\end{itemize}
	\item A kék szín
	\begin{itemize}
		\item hexadecimális alakja: \verb|0000ff|
		\item bináris alakja: $\begin{array}{c|c|c} 00000000 & 00000000 & 11111111 \end{array}$
	\end{itemize}
\end{description}

Ezek után az értékeket egyesítjük.
\begin{description}
	\item A fehér szín
	\begin{itemize}
		\item hexadecimális alakja: \verb|ffffff|
		\item bináris alakja: $\begin{array}{c|c|c} 11111111 & 11111111 & 11111111 \end{array}$
	\end{itemize}
\end{description}

\subsection{Képmanipulálás}
A kép adott pixelének piros, zöld, kék értékeit utólag visszanyerhetjük. Ezeket az értékeket módosíthatjuk, új képeket állíthatunk elő.

\subsection{Filter}
A filterezéshez szükség van egy filter mátrixra. Ez a mátrix a képhez hasonlóan 2 dimenziós, és egész számokat tartalmaz.

A mátrixra a következők érvényesek: 
\begin{itemize}
	\item Négyzetes mátrix, melynek mérete páratlan. ($3 \times 3$, $5 \times 5$, ...)
	\item Az elemek összege, ha: 
	\begin{itemize}
		\item 1: A keletkező kép ugyanolyan világos lesz, mint az eredeti.
		\item nagyobb, mint 1: A keletkező kép világosabb lesz, mint az eredeti.
		\item kisebb, mint 1: A keletkező kép sötétebb lesz, mint az eredeti.
	\end{itemize}
\end{itemize}
Egy élkereső mátrix például a következő:
\[
\begin{matrix*}[r]
	-1 & -1 & -1 \\
	-1 & 8 & -1 \\
	-1 & -1 & -1
\end{matrix*} \]
A mátrixok hatását később módosíthatjuk, például ha az elemek összege nem 1, de ugyanolyan világos képet szeretnénk, a számoláskor mindig beszorzunk az elemek összegének reciprokával.

Az algoritmus abból áll, hogy a kép minden egyes pixelére vesszük a filter mátrixszal vett szorzatok összegét. A szorzatok egy-egy szín értéket tartalmaznak, amik az adott pixel, vagy annak szomszédainak a filter mátrix segítségével módosított értékek. Ezt az eljárást konvolúciónak is nevezik. \cite{filter}

\section{Megoldási terv}
A program Model/Nézet architektúra alapján készül. Ez által szétválik az alkalmazáslogika és a grafikus felület. Később új grafikus felületek készíthetőek, a meglévő mellé vagy lecserélésére.

\subsection{Rendszer architektúra}
A program szerkezetének leegyszerűsített nézete a következő ábrán látható:

\begin{figure}[H]
	\includegraphics[width = \linewidth]{diagrams/packages.png}
	\caption{A program szerkezete}
\end{figure}

\subsubsection{GUI}
A program grafikus felület segítségével kommunikál a felhasználóval gombok, menük segítségével, valamint az előállított képek megjelenítésére szolgál.

Itt jelenik meg továbbá a transzformációkról gyűjtött információ (méretek, idő, stb.).

\subsubsection{Backend}
Ez a komponens tartalmazza az alkalmazáslogikát.

Itt tároljuk a betöltött, valamint a módosított képet, az OpenCL platform információit.

\subsubsection{Filter}
Tárolja az eredeti képet reprezentáló tömböt (mátrix), valamint előállítja a transzformált tömböt.

\subsubsection{OpenCL}
Definiálja a párhuzamos algoritmust, előállítja a transzformált tömböt OpenCL segítéségével.

\subsection{Felhasználói felület terve}

\begin{figure}[H]
	\includegraphics[width = \linewidth]{pics/terv/kepernyokep.png}
	\caption{Felhasználói felület terve}
\end{figure}

\subsubsection{Menü}
A menüben fogjuk elhelyezni a képek betöltéséhez, mentéséhez, transzformálásához szükséges menüpontokat.

\subsubsection{Eszköztár}
Nyomógombokat helyezünk el, melyek segítségével a felhasználó nagyíthat, kicsinyíthet a képen, illetve átemelheti a módosított képet további transzformáció végrehajtásához.

\subsubsection{Táblázat}
Összesítjük a transzformáció során kapott adatokat.
\begin{itemize}
	\item Kép szélessége
	\item Kép magassága
	\item Transzformáció típusa
	\item A transzformáció párhuzamos algoritmussal történt-e
	\item A transzformáció végrehajtásához szükséges idő
\end{itemize}

\subsubsection{Státusz sor}
Itt jelenítjük meg az OpenCL beállítása után a kiválasztott platformot és eszközt, valamint transzformáció közben egy folyamatjelző segítségével követhetjük nyomon a transzformáció végrehajtását.

\subsubsection{Eseménykezelések}
A felhasználó a következő eseményeket válthatja ki:
\begin{itemize}
	\item Menü menüpontjai
	\item Eszköztár gombjai
	\item Megszakítógomb
	\item Görgetés, mely megfelel az eszköztáron lévő gombok segítségével történő nagyításnak, kicsinyítésnek
\end{itemize}

\subsection{Osztályszerkezet}

\begin{figure}[H]
	\includegraphics{diagrams/classes.png}
	\caption{Osztályszerkezet, áttekintő ábra}
\end{figure}

\subsubsection{MainWindow}
Az alkalmazás grafikus rétegének fő osztálya a MainWindow. \\

\begin{figure}[H]
	\includegraphics{diagrams/class_mainwindow.png}
	\caption{MainWindow osztálydiagram}
\end{figure}

A Modell/Nézet architektúrának megfelelően tartalmaz egy Backend adattagot, amely az alkalmazáslogikáért felel.

A központi widget (centralWidget) fogja tartalmazni a megjelenített képeket, valamint a táblázatot az adatokról.

A transzformáció folyamatának jelzéséhez szükség lesz egy folyamatjelzőre (progressBar), valamint a transzformáció megszakításához egy gombra.

Dialógusablakban (clDialog) állíthatjuk be az OpenCL platformot.

Eszköztáron gombok, menük (toolbar, menu) segítségével vezérelheti a programot a felhasználó. Ezekhez eseménykezelőket (listeners) társítunk, melyek elindítják a választott transzformációt (do\_transformation).

\subsubsection{Backend}
A Modell/Nézet architektúra Modell része.

\begin{figure}[H]
	\includegraphics{diagrams/class_backend.png}
	\caption{Backend osztálydiagram}
\end{figure}

Tárolni fogja az eredeti, valamint a módosított képet. A módosítótt kép előállításáért a filter adattag felel. Az OpenCL platform, device beállítása is itt történik.

\subsubsection{Filter}
Ez az osztály felel a módosított kép előállításáért.

\begin{figure}[H]
	\includegraphics{diagrams/class_filter.png}
	\caption{Filter osztálydiagram}
\end{figure}

A bias, factor, filterMatrix, filterSize paraméterek definiálják a végrehajtandó transzformációt.

Az originalData adattagban egész számokat tartalmazó tömbként tároljuk az eredeti kép pixel értékeit, valamint tároljuk a kép szélességét és magasságát.

A use\_filter() metódus állítja elő a módosított értékeket.

\subsubsection{OpenCL}
A párhuzamos transzformálásért felel.

\begin{figure}[H]
	\includegraphics{diagrams/class_opencl.png}
	\caption{OpenCL osztálydiagram}
\end{figure}

A platform, device tagokat a felhasználó állítja be, a backend továbbítja.

Bufferekben tároljuk a feldolgozandó adatot, a szükséges filter mátrixot, valamint a keletkező eredménynek is szüksége van egy bufferre.

A párhuzamos algoritmust egy OpenCL kernelben definiáljuk, aminek a forráskódját szövegként kell betölteni.

A context a bufferekért felel, a CommandQueue pedig a buffer írási-olvasási műveleteiért.

\section{Megvalósítás}

\subsection{Felhasznált technológiák}

\subsubsection{Párhuzamosítás}
Párhuzamosításra felhasználható például az OpenCL (Open Computing Library), mely egy C/C++ keretrendszer. Az adott hardver képességeitől függően több virtuális szálon dolgozhatunk, CPU-n vagy GPU-n.

Az OpenCL keretrendszerben adat-párhuzamosítási feladatokat oldhatunk meg. Egy adott minden elemére szeretnénk egy megadott számítást elvégezni.

Az egyszerre feldogozható elemek száma véges, ezek a munkacsoportok (work-groups), elemei a munkaelemek (work-items).

\begin{figure}[H]
	\includegraphics[width = \linewidth]{pics/opencl_model.jpg}
	\caption{Az OpenCL elemei: munkacsoportok, munkaelemek}
\end{figure}

Az OpenCL adat-párhuzamosítási program modellje hierarchikus, amit kétféleképpen adhatunk meg:
\begin{itemize}
	\item Explicit: a fejlesztő adja meg a párhuzamosan számolható munkaelemek számát, valamint azt is, hogy ezek az elemek hogyan alkotnak munkacsoportokat.
	\item Implicit: a fejlesztő csak a munkaelemek számát adja meg, az OpenCL szabályozza a munkacsoportok számát, valamint a munkacsoportokba történő besorolást.
\end{itemize}
Bár az OpenCL nyílt szabványú, amit a Khronos Group kezel, számos gyártó készít saját implementációt.

Szakdolgozatomban az AMD (Advanced Micro Devices) gyártó által készített verzióval dolgozom.

\subsubsection{Grafikus felület}
A Qt egy cross-platform keretrendszer, amely széles körben használt grafikus felületű alkalmazások fejlesztésére.

A Qt C++ alapú, de jelentősen kibővíti azt.

Mivel az OpenCL is C++-ban programozható, a kettő egyesítése megvalósítható.

\subsubsection{Alkalmazás logika}
A Qt keretrendszer nem csak grafikus felületek készítésére használható. Tömérdek mennyiségű könyvtár áll benne rendelkezésre a különböző feladatokra.

\paragraph{QImage}
A QImage\cite{qimage} osztály hardver független kép reprezentációt használ, amely segítségével közvetlenül hozzáférhetünk a pixel adatokhoz. Az osztály I/O műveletekre és közvetlen pixel eléréshez/manipulációhoz lett tervezve, optimalizálva.

Fontosabb műveletei:
\begin{itemize}
	\item \verb|bits|: Egy mutatót ad az első pixelre
	\item \verb|copy|: Kép egy részének másolása
	\item \verb|pixel|: Pixel lekérése egy adott sorban és oszlopban
	\item \verb|setPixel|: Pixel beállítása egy adott sorban és oszlopban
\end{itemize}

\subsection{Kép pixeljeinek lekérése}
A \verb|QImage::bits| metódus segítéségével egy mutatót kapunk az első pixel elemre. A kép szélességének és magasságának segítségével megalkothatnánk a bevezetésben említett mátrixot.

Azonban egyszerűbb és hatékonyabb, ha meghagyjuk ezt a sor folytonos ábrázolást és a transzformáció végrehajtásakor számoljuk ki, hogy az elem lényegében melyik oszlopban és sorban is található.

Továbbá párhuzamosításkor az OpenCL számára könnyen létrehozható egy buffer az adott mutató segítségével.

\subsubsection{Példa - sorfolytonos ábrázolás}
\[
\begin{pmatrix*}[r]
	0 & 3 \\
	5 & 8 
\end{pmatrix*} 
\Rightarrow
\begin{array}{c|c|c|c}
	0 & 3 & 5 & 8
\end{array}
\]

\subsection{A filter algoritmus}
\subsubsection{Pszeudokód}
\begin{SaveVerbatim}{myVerb}
	minden x-re 1..kép_szélessége
	    minden y-ra 1..kép_magassága
	        piros, zöld, kék = 0.0
	        minden filterX-re 1..filter_szélessége
	            minden filterY-ra 1..filter_magassága
	                képX = (x - filter_szélessége / 2 + filterX + kép_szélessége) % kép_szélessége
	                képY = (y - filter_magassága / 2 + filterY + kép_magassága) %kép_magassága
	                piros += kép[képX, képY].piros * filter[filterX, filterY]
	                zöld += kép[képX, képY].zöld * filter[filterX, filterY]
	                kék += kép[képX, képY].kék * filter[filterX, filterY]
	        
	        piros, zöld, kék értékek [0..255] közötti értékekké való módosítása
	        
	        módosított_kép[x, y].piros = piros
	        módosított_kép[x, y].zöld = zöld
	        módosított_kép[x, y].kék = kék
\end{SaveVerbatim}
\resizebox{0.95\textwidth}{!}{\BUseVerbatim{myVerb}}%

\subsubsection{Sorfolytonos ábrázolás}
Mivel C++-ban a tömbök 0-val kezdődően indexelődnek, a tömb indexe alapján a következőképp határozhatjuk meg az adott indexű elem képben elfoglalt helyét:
\begin{itemize}
	\item oszlop = index \% kép\_szélessége
	\item sor = index / kép\_szélessége
\end{itemize}

Az sor, oszlop számításnál egész számokat kapunk eredményként.

Ezáltal az algoritmus a következőképp módosul:
\begin{verbatim}
    minden i-re 1..kép_szélessége * kép_magassága
        x = i % kép_szélessége
        y = i / kép_szélessége
        ...
\end{verbatim}

\subsubsection{Párhuzamos algoritmus}
Az OpenCL programok két részre bonthatók:
\begin{itemize}
	\item a kód ami az eszközön (device) fut (GPU, CPU)
	\item a kód ami a gazdán (host) fut (CPU)
\end{itemize}

\paragraph{Eszköz}
Az eszközön futtatjuk a párhuzamos kódot. Erre speciális metódusokat, úgynevezett kernelt kell írjunk, az OpenCL programozási nyelvén.

Az OpenCL egy C99 alapú nyelv néhány bővítéssel és megszorítással.

\paragraph{Gazda}
A gazda program felel az eszköz kód végrehajtásáért. Ilyen például az adatok feltöltése az adott eszközre, a kernel lefuttatása, adatok kiolvasása.

Ezt C vagy C++ nyelven írhatjuk az OpenCL API (Application Programming Interface) segítségével.

\paragraph{Kernel}
A kernelre a következőképp gondolhatunk: adott a korábban említett filter algoritmus, mely egy ciklus segítségével minden tömbelemre kiszámol egy értéket. A kernel a ciklusmagon belüli rész.
Az OpenCL virtuálisan végtelen szálon képes dolgozni. A betöltött bufferen, amit egy vektorként képzelhetünk el, lekérdhetjük a kernelen belül, hanyadik indexű elemet számoljuk ki épp. Ez a korábbi algoritmusnál az i ciklusváltozó. Az algoritmus további része változatlan.

Az adott eszköz képességeitől függően véges számú virtuális szál indul, mikor egy szál befejezte a működést, automatikusan keres egy következő feldolgozandó elemet. Az elemek feldolgozási sorrendje nem determinisztikus.

\paragraph{Lépések}
\begin{enumerate}
	\item Platform beállítása
	\item Eszköz (Device) beállítása
	\item Kontextus (context) létrehozása az eszközön
	\item Kernel beolvasása
	\item Program létrehozása a kontextuson a kernel fordításával
	\item Adatok betöltése bufferként
	\item A kernel argumentumainak megadása
	\item CommandQueue létrehozása, amelyben a kernel számára végrehajtandó parancsokat tároljuk
	\item A CommandQueue-n megadjuk, hogy futtassa le a kernelt az argumentumokra
	\item Az eredmény kiolvasása
\end{enumerate}

\subsection{A felhasználói felület}

\subsubsection{Képek megjelenítése}
Qt-ban képek megjelenítéséhez használhatunk label-öket. Viszont nagy képek betöltésekor vagy csak egy részletet tudunk mutatni, vagy a nagy méret miatt kilóg a képernyőről.

Ennek kiküszöbölésére készítettem egy \verb|ImageViewer| widgetet, ami egy görgethető felületen jeleníti meg a képet.

\paragraph{Képek szinkronizálása}
Az eredeti és a módosított képet szinkronban kell tartani mind nagyítás, mind a görgetősávon bekövetkezett mozgatás esetén. \\

\subparagraph{Görgetősáv}
A görgetősávokról lekérdezhető az aktuális pozíció, amit a központi widget felé továbbítunk, ami kiküldi a másik képmegjelenítőnek a pozíciót és beállítja a görgetősávnak.

\subparagraph{Nagyítás, kicsinyítés}
A méretváltozást a központi widget továbbítja, amely mind a két képmegjelenítőnek kiküldi, hogy nagyítás illetve kicsinyítés történt.
Ahhoz, hogy görgővel is tudjunk nagyítani, illetve kicsinyíteni, a főablakban kell lekezelnünk a görgetés eseményét. Ugyanis a képmegjelenítő görgetősávja előbb kapja meg a görgetés eseményt, mint mi fel tudnánk dolgozni, és ennek ez az eredménye, hogy fel-le, (balra-jobbra) mozgás után történne csak meg a kívánt nagyítás, kicsinyítés.

\subsubsection{Adatok megjelenítése}
Qt-ban rendelkezésünkre áll egy \verb|QTableWidget| amellyel táblázatban jeleníthetünk meg adatokat.

\subsubsection{Központi widget}
Az előbb említett két képmegjelenítő, valamint a táblázat widgetet tartalmazza a központi widget, amely majd a főablakban kerül beállításra.

\subsubsection{Főablak}
Az alkalmazás főablaka egy \verb|QMainWindow|, melyben definiálhatunk menüket, eszköztárakat, státuszsorokat, widgeteket, stb. A főablaknak egy központi widgettel rendelkeznie kell, amely az előbb említett központi widget lesz.

Definiálunk egy folyamatjelzőt (\verb|QProgressBar|), eszköztárat (\verb|QToolBar|) és rá nyomógombokat (\verb|QPushButton|), menüt (\verb|QMenu|) amihez akciókkal (\verb|QAction|) rendelünk eseményeket.

Minden eseményt a főablak fogad és továbbítja a megfelelő elemeknek.

\paragraph{Görgetés}
Ahhoz, hogy a szinkronizálás pontban leírtak működjenek, a főablakban kell definiálnunk egy event filtert (esemény szűrő). A főablak kap meg legelőször minden eseményt és ő továbbítja az eseményt az esetleges widgeteknek. A módszer lényege, hogy érzékeljük, hogy görgetés esemény történt, és közvetlenül a központi widgetnek továbbítjuk ezt az adatot, ahol az eseménykezelő hatására megtörténik a nagyítás, kicsinyítés.

\subsubsection{Dialógus ablakok}
Dialógus ablakok segítségével kérhetünk be további adatokat a felhasználóktól.

\paragraph{OpenCL beállítása}
Az OpenCL platform, device beállítására egy külön osztály felel, mely a \verb|QDialog| osztályból származik. Egy jelölőnégyzet segítségével engedélyezhetjük, vagy kikapcsolhatjuk az OpenCL-t. Engedélyezés esetén rádiógombokkal választhatunk a rendelkezésre álló platformok és eszközök között.

\paragraph{Blur, Motion blur paraméterezése}
E két transzformáció beállításához egy közös ős osztály áll rendelkezésre, a \verb|TransformationDialog|. Ez az osztály tartalmazza a képet, és a labelt amelyben a kép megjelenik.

\subparagraph{Blur}
A paraméterezést egy \verb|QSlider| segítségével valósíthatjuk meg, amelyben háromféle elmosás közül választhatunk. Minél nagyobb az elmosás, annál nagyobb a filterhez felhasznált mátrix mérete.

\subparagraph{Motion blur}
A paraméterezés rádiógombokkal valósul meg. Kiválasztható az irány: 45 fokos, vízszintes, stb.

\subsubsection{Folyamatjelző}
A transzformáció állapotának nyomon követéséhez van szükség folyamatjelzőre. A pillanatnyilag feldolgozott pixelek számával jól reprezentálható a feldolgozott és a hátralévő mennyiség.

Azaz
\begin{itemize}
	\item 0\%: a transzformáció még nem indult el, egy pixel sem került feldolgozásra
	\item 100\%: a transzformáció minden pixelre megtörtént, azaz összesen a kép szélessége * kép magassága pixelt dolgoztunk fel
\end{itemize}

\paragraph{Szekvenciális algoritmus}
Szekvenciális esetben minden iteráció után küldhetünk jelet a folyamatjelzőnek.

\paragraph{Párhuzamos algoritmus}
Párhuzamos esetben, hogy a folyamatjelzőt értesíteni tudjuk a már feldolgozott adatokról, ciklusokban töltjük fel az adatokat az eszközre és végezzük a transzformációt. Erre csak a folyamatjelző miatt van szükség, ugyanis az OpenCL magától elvégezné ezt a tevékenységet.

\subparagraph{Transzformáció felbontása}
Minden eszközön van egy maximálisan indítható virtuális szál mennyiség, ami lekérdezhető a \verb|CL_DEVICE_MAX_WORK_GROUP_SIZE| makró segítségével. Ha a pixelek száma kisebb, mint ez a mennyiség, akkor egyben végrehajtható a transzformáció. Ellenkező esetben ahányszor megvan ez a mennyiség a pixelek számában, annyiszor fogjuk elvégezni a transzformációt a makróban megadott mennyiségre, mindig az adott számmal eltolva. Végül a maradékra is elvégezzük a transzformációt.

\subsection{Hibák kezelése}
A hibákat három csoportba sorolhatjuk:
\begin{itemize}
	\item OpenCL hiba: az OpenCL beállítása, működése során lép fel. Erre létrehozunk egy új kivétel osztályt.
	\item Futási idejű hiba. \verb|std::runtime_error| segítségével dobunk kivételeket.
	\item Minden más hiba. 
\end{itemize}

\subsubsection{OpenCL hibák kezelése}
Létrehozunk egy \verb|OpenCLException| osztályt, ami a \verb|std::exception| osztályból származik. Felveszünk egy errorCode adataggot, valamint egy ennek lekérdezésére szolgáló publikus műveletet, így a hibaüzenetben feltüntethetjük az OpenCL hibakódot, ami alapján diagnosztizálható a hiba.

\subsubsection{Futási idejű hibák}
Futási idejű hibákat a \verb|std::runtime_error| kivétel segítségével dobhatunk, konstruktorában megadhatjuk a hiba okát. Elkapásakor ezt megjelenítjük, és továbbítjuk a felhasználó felé.

\subsubsection{Egyéb hibák}
Minden más hiba esetén tájékoztatjuk a felhasználót, hogy a program le fog állni, majd az alkalmazás leállítja magát.

\section{Tesztelés}
A program működése során a következőket kell ellenőrizni:
\begin{itemize}
	\item A párhuzamos és szekvenciális transzformáció ugyanazt a képet eredményezik?
	\item Kis képek esetén a program megfelelően működik?
	\item Nagy képek esetén a program megfelelően működik?
	\item A program milyen hardvereken működik? (Szakdolgozatomban az AMD féle OpenCL könyvtár van mellékelve Windows verzióhoz)
\end{itemize}

\subsection{A szekvenciális és párhuzamos transzformáció összehasonlítása}
A kétféle transzformáció egyezőségének ellenőrzéséhez egy külön segédprogram áll rendelkezésre, amely a \verb|test| mappában található. 

A futtatható állomány mellé a \verb|sequential| mappába gyűjtsük össze a szekvenciális transzformációk eredményeit, a \verb|parallel| mappába a párhuzamos transzformációk eredményeit. 9 darab fájlt helyezzünk el mindkét könyvtárba, ügyelve a következőkre:
\begin{itemize}
	\item 1.jpg : horizontális élkeresés
	\item 2.jpg : vertikális élkeresés
	\item 3.jpg : 45 fokos élkeresés
	\item 4.jpg : összes él keresése
	\item 5.jpg : elmosás (az alapértelmezett, középső opcióval történő transzformálást válasszuk)
	\item 6.jpg : mozgó elmosás (az alapértelmezett, első opciót válasszuk)
	\item 7.jpg : élesítés
	\item 8.jpg : túlzó élek
	\item 9.jpg : dombornyomat
\end{itemize}

A program amennyiben eltérést talál, kijelzi, hogy melyik képnél és melyik pixelnél van eltérés.

\subsubsection{Eredmények}
A tesztek alapján a szekvenciális és párhuzamos transzformáció eredménye nem különbözik.

\subsection{Mérések}
A mérések esetében mindig az élesítés transzformáció adatait gyűjtöttük össze.
A táblázatban lévő időegységek milliszekundumban értendőek.

\subsubsection{1. mérés}
OS = Windows \\
CPU = AMD Phenom II N850 Triple-processor \\
GPU = AMD Mobility Radeon HD 5650

\begin{table}[H]
	\begin{tabular}{c | c | c | c | c}
		Szélesség & Magasság & OpenCL nélkül & OpenCL GPU & OpenCL CPU \\
		320 & 240 & 340 & 420 & 210 \\
		1366 & 768 & 414 & 271 & 251 \\
		1900 & 1200 & 892 & 321 & 578 \\
		3072 & 1728 & 2033 & 736 & 1220 \\
	\end{tabular}
	\caption{1. mérés adatai}
\end{table}

\subsubsection{2. mérés}
OS = Windows \\
CPU = Intel Core i5-3570K \\
GPU = AMD Radeon R9 280X

\begin{table}[H]
	\begin{tabular}{c | c | c | c | c}
		Szélesség & Magasság & OpenCL nélkül & OpenCL GPU & OpenCL CPU \\
		320 & 240 & 9 & 10 & 5 \\
		1366 & 768 & 108 & 33 & 50 \\
		1900 & 1200 & 231 & 61 & 99 \\
		3072 & 1728 & 535 & 140 & 216 \\
	\end{tabular}
	\caption{2. mérés adatai}
\end{table}

\subsubsection{3. mérés}
OS = Windows \\
CPU = AMD Athlon II P320 Dual-Core Processor

\begin{table}[H]
	\begin{tabular}{c | c | c | c}
		Szélesség & Magasság & OpenCL nélkül & OpenCL CPU \\
		320 & 240 & 28 & 29 \\
		1366 & 768 & 404 & 276 \\
		1900 & 1200 & 896 & 646 \\
		3072 & 1728 & 2047 & 1904 \\
	\end{tabular}
	\caption{3. mérés adatai}
\end{table}

\subsubsection{4. mérés}
OS = Windows \\
CPU = Intel Core i5-4570 \\
GPU = Nvidia GeForce GTX 760

\begin{table}[H]
	\begin{tabular}{c | c | c | c | c}
		Szélesség & Magasság & OpenCL nélkül & OpenCL GPU & OpenCL CPU \\
		320 & 240 & 8 & 3 & 2 \\
		1366 & 768 & 111 & 16 & 36 \\
		1900 & 1200 & 242 & 31 & 78 \\
		3072 & 1728 & 534 & 73 & 272 \\
	\end{tabular}
	\caption{4. mérés adatai}
\end{table}

\subsubsection{Összefoglalás}
A táblázatokban lévő eredmények alapján jól kivehető, hogy kis képek esetén nincs, vagy minimális a gyorsulás. Ennek az az oka, hogy az OpenCL működése során bizonyos időt felemészt az adatok bufferbe mentése, feltöltése az eszközre, és visszaolvasása.

Továbbá a transzformáció végrehajtásakor megfigyelhető, hogy szekvenciális esetben is több CPU mag dolgozik. Ennek az oka az, hogy az operációs rendszer képes több magra szétosztani a feladatot.

Általánosságban azonban elmondható, hogy gyorsabb az OpenCL féle változat és ez a legjobban a GPU felhasználásánál jön elő.

Habár minden esetben az AMD féle OpenCL \verb|dll| fájl volt használva, Nvidia és Intel platformokon is megfelelően működött a program.